package com.heima.admin.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.heima.admin.mapper.AdChannelMapper;
import com.heima.admin.mapper.AdSensitiveMapper;
import com.heima.admin.service.WemediaNewsAutoScanService;
import com.heima.aliyun.GreenImageScan;
import com.heima.aliyun.GreenTextScan;
import com.heima.common.constants.message.ArticleForEsConstants;
import com.heima.common.exception.CustomException;
import com.heima.feigns.author.ArticleFeign;
import com.heima.feigns.wemedia.WemediaFeign;
import com.heima.model.admin.pojo.AdChannel;
import com.heima.model.admin.pojo.AdSensitive;
import com.heima.model.article.dto.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojo.WmNews;
import com.heima.model.wemedia.pojo.WmUser;
import com.heima.utils.common.SensitiveWordUtil;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author TheCai
 * @date 2021/5/30 22:32
 */
@Service
@Slf4j // logback
public class WemediaNewsAutoScanServiceImpl implements WemediaNewsAutoScanService {

  @Value("${file.oss.web-site}")
  String webSite;

  @Autowired WemediaFeign wemediaFeign;

  /**
   * 自动审核发布文章方法
   *
   * @param wmNewsId
   */
  @GlobalTransactional
  @Override
  public void autoScanByMediaNewsId(Integer wmNewsId) {
    log.info("自动发布文章方法被触发===========当前处理的文章id======= {}", wmNewsId);
    // 1.检查文章id
    if (wmNewsId == null) {
      log.error("文章自动审核失败,id为空");
      throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
    }
    // 2.根据文章id远程获取文章信息
    WmNews wmNews = wemediaFeign.findByID(wmNewsId);
    if (wmNews == null) {
      log.error("文章自动审核失败,远程查询自媒体文章失败");
      throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST, "对应文章不存在");
    }
    // 3.检查文章状态
    Short status = wmNews.getStatus();
    // 4.如果文章状态为4或8且发布时间小于或等于当前时间  发布  ==>>调用发布方法
    if ((status.equals(WmNews.Status.ADMIN_SUCCESS.getCode())
            || status.equals(WmNews.Status.SUCCESS.getCode()))
        && wmNews.getPublishTime().getTime() <= System.currentTimeMillis()) {
      log.info("自媒体文章被发布===========当前处理的文章id======= {}", wmNewsId);
      publishArticle(wmNews);
      return;
    }
    // 5.如果文章状态为 1  代表需要审核该文章
    if (status.equals(WmNews.Status.SUBMIT.getCode())) {
      // 5.1 TODO 抽取文章内容中的文本和图片
      Map<String, Object> contentMap = handleTextAndImages(wmNews);// 抽取文章内容中的纯文本和图片

      // 5.2 TODO DFA自管理敏感词审核

      //审核失败:修改文章状态为2
      String text = contentMap.get("text").toString();
      Boolean isSensitive = handleSensitive(text, wmNews);// DFA自管理敏感词审核
      if (!isSensitive) return;
      log.info("=======当前处理的文章通过DFA敏感词审核======当前处理的文章id======= {}", wmNewsId);

      // 5.3 TODO 阿里云文本审核
    boolean isTextScan = handleTextScan(contentMap.get("text").toString(),wmNews); // 阿里云文本审核
      if (!isTextScan) return;
      log.info("=======当前处理的文章通过阿里云文本审核======当前处理的文章id======= {}", wmNewsId);

      // 5.4 TODO 阿里云图片审核


      //   审核失败:修改文章状态为2  不确定:状态改为3 //审核失败:修改文章状态为3

      boolean isImagesScan = handleImagesScan((List)contentMap.get("images"),wmNews);// 阿里云图片审核

      if (!isImagesScan) return;
      log.info("=======当前处理的文章通过阿里云图片审核======当前处理的文章id======= {}", wmNewsId);
      // 5.5 TODO 如果发布时间大于当前时间,审核通过 :状态8
      if (wmNews.getPublishTime().getTime()>System.currentTimeMillis()){
        updateWmNews(wmNews,(short) 8,"审核通过");
        return;
      }


      // 5.6 TODO 如果发布时间小于等于当前时间  发布文章 ==> 调用发布时间
      publishArticle(wmNews);

    }

  }
  @Autowired
  GreenImageScan greenImageScan;
  /**
   * 阿里云图片审核
   * @param wmNews
   * @return
   */
  private boolean handleImagesScan(List images, WmNews wmNews) {
    Boolean flag=true;
    try {
      Map map = greenImageScan.imageUrlScan(images);
      //阿里云给出的审核结果,建议
      String suggestion = map.get("suggestion").toString();
      if ("review".equals(suggestion)){
        flag=false;
        updateWmNews(wmNews,(short) 3,"图片中包含不确定因素,转为人工审核");

      }
      if ("block".equals(suggestion)){
        flag=false;
        updateWmNews(wmNews,(short) 2,"图片违规");
      }
    } catch (Exception e) {
      e.printStackTrace();
      flag=false;
      updateWmNews(wmNews,(short) 3,"阿里云审核失败,转为人工审核");
    }


    return flag;
  }



  @Autowired
  GreenTextScan greenTextScan;
  /**
   * 阿里云文本审核
   * @param wmNews
   * @return
   */
  private boolean handleTextScan(String text, WmNews wmNews) {
    Boolean flag=true;
    try {
      Map map = greenTextScan.greenTextScan(text);
    //阿里云给出的审核结果,建议
      String suggestion = map.get("suggestion").toString();
      if ("review".equals(suggestion)){
        flag=false;
        updateWmNews(wmNews,(short) 3,"内容中包含不确定因素,转为人工审核");

      }
      if ("block".equals(suggestion)){
        flag=false;
        updateWmNews(wmNews,(short) 2,"文本中包含敏感词汇");
      }

    } catch (Exception e) {
      e.printStackTrace();
      flag=false;
      updateWmNews(wmNews,(short) 3,"阿里云审核失败,转为人工审核");
    }


    return flag;
  }


  @Autowired
  AdSensitiveMapper adSensitiveMapper;//报错很烦,改了检查状态

  /**
   * DFA自审核敏感词
   * @param text  上传的文本
   * @param wmNews  文章
   * @return 有没有
   */
  private Boolean handleSensitive(String text, WmNews wmNews) {
    Boolean flag=true;

    List<AdSensitive> sensitivesList = adSensitiveMapper.selectList(null);
    //敏感词内容列表
    List<String>  sensitiveList= sensitivesList.stream().map(AdSensitive::getSensitives).collect(Collectors.toList());
    //基于DFA算法进行敏感词检测
    SensitiveWordUtil.initMap(sensitiveList);
    //塞入需检测的
    Map<String, Integer> sensitiveMap = SensitiveWordUtil.matchWords(text);
    if (CollectionUtil.isNotEmpty(sensitiveMap)){
      //有敏感词
      flag=false;
      //   审核失败:修改文章状态为2  不确定:状态改为3
      //搞个敏感词方法
      updateWmNews(wmNews,(short)2,"文章内容中包含敏感词"+sensitiveMap);
    }
    return flag;
  }

  /**
   * 修改文章状态
   * @param wmNews 文章
   * @param status 状态
   * @param reason 原因
   */
  private void updateWmNews(WmNews wmNews, short status ,String reason) {
    wmNews.setStatus(status);
    wmNews.setReason(reason);
    ResponseResult responseResult = wemediaFeign.updateWmNews(wmNews);
    if (!responseResult.getCode().equals(0)){
      log.error("远程修改 自媒体文章状态失败 {}",responseResult.getErrorMessage());
      throw new CustomException(AppHttpCodeEnum.SERVER_ERROR,responseResult.getErrorMessage());
    }
  }

  /**
   * 抽取文章内容中的纯文本和图片
   *
   * @param wmNews
   * @return Map 图片带前缀,因为图片路径要交给阿里云去审核,所以需要完整路径
   */
  private Map<String, Object> handleTextAndImages(WmNews wmNews) {
    Map<String, Object> result = new HashMap<>();
    // 1 获取文章内容并转换对象
    String content = wmNews.getContent();
    if (StringUtils.isBlank(content) || content.length() > 10000) {
      log.error("文章自动审核失败,自媒体文章内容为空");
      throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"自媒体文章内容为空");
    }
    List<Map> contentMap = JSON.parseArray(content, Map.class);
    // 第一大步抽取文本内容
    // 2 解析内容文本 Stream 流优化 --> String,,,把_hmtt_拼接进去
    String contentsTxet =
        contentMap.stream()
            .filter(map -> map.get("type").equals("text"))
            .map(map -> map.get("value").toString())
            .collect(Collectors.joining("_hmtt_"));
    // 中美
    // 国家英语 _hmtt_  美国？

    // 文章标题 参与审核,把标题拼接在文本里即可
    contentsTxet = wmNews.getTitle() + "_hmtt_" + contentsTxet;

    // 第二大步抽取图片内容
    // 3 解析内容图片
    List<String> collectImages =
        contentMap.stream()
            .filter(map -> map.get("type").equals("image"))
            .map(map -> map.get("value").toString())
            .collect(Collectors.toList());
    // 4 处理封面前缀 1.jpg 2.jpg
    // 添加到内容图片列表
    if (StringUtils.isNotBlank(wmNews.getImages())) {
      Arrays.stream(wmNews.getImages().split(","))
          .map(url -> webSite + url)
          .forEach(collectImages::add);
    }
    //封面图片可能和内容图片重复,做一下去重操作
    collectImages = collectImages.stream().distinct().collect(Collectors.toList());
    // 5 封装结果
    result.put("text", contentsTxet);
    result.put("images", collectImages);
    return result;
  }

  /**
   * 发布文章方法
   *
   * @param wmNews
   */
  private void publishArticle(WmNews wmNews) {
   ResponseResult responseResult = saveApArticle(wmNews);
   if (!responseResult.getCode().equals(0)){
     log.error("文章自动审核失败 , 远程调用发布文章接口失败");
     throw new CustomException(AppHttpCodeEnum.SERVER_ERROR,"远程保存文章失败");
   }

   //用于关联保存成功的article文章
  wmNews.setArticleId((Long)responseResult.getData());
  updateWmNews(wmNews,(short)9,"发布成功");
//=================================kfka发消息,加的===================
    Map map = new HashMap();
    map.put("articleId",wmNews.getArticleId());
    kafkaTemplate.send(ArticleForEsConstants.ARTICLE_ELASTICSEARCH_ADD, JSONUtil.toJsonStr(map));
  log.info("文章已自动发布,并向搜索服务发送 添加文章通知");
  }

  @Autowired
  KafkaTemplate kafkaTemplate;

  @Autowired
  AdChannelMapper channelMapper;

  @Autowired
  ArticleFeign articleFeign;
  /**
   * 远程保存文章
   * 保存文章三表信息  article  article_config  article_content
   * @param wmNews
   * @return
   */
  private ResponseResult saveApArticle(WmNews wmNews) {
    ArticleDto articleDto = new ArticleDto();
  //封装articleDto
    BeanUtils.copyProperties(wmNews,articleDto);
//拷贝是会将id拷贝到articleDto的id中手动制空
    articleDto.setId(null);

    Long articleId = wmNews.getArticleId();

//有可能wmNews之前发布过,后来下架了
    if (articleId!=null){
      //设置到id字段中,远程调用是,如果有这个id就修改,如果,没有就添加
      articleDto.setId(articleId);
    }
    //封面布局
    articleDto.setLayout(wmNews.getType());
    articleDto.setFlag((byte)0);

    //设置作者信息,根据wmNews关联的wmuserId查询出wmuser信息(name和作者标的name字段是一致的)
    WmUser wmUser = wemediaFeign.findWmUserById(wmNews.getUserId());

    if (wmUser==null){
      log.error("文章自动审核失败 , 获取关联自媒体用户失败");
      throw new CustomException(AppHttpCodeEnum.SERVER_ERROR,"获取关联自媒体用户失败");
    }
    articleDto.setAuthorName(wmUser.getName());
    //设置频道信息
    AdChannel adChannel = channelMapper.selectById(wmNews.getChannelId());
    if (adChannel==null){
      log.error("文章自动审核失败 , 获取关联频道信息失败");
      throw new CustomException(AppHttpCodeEnum.SERVER_ERROR,"获取关联频道信息失败");
    }
   articleDto.setChannelName(adChannel.getName());

    //远程调用文章微服务,保存文章三表信息
    return articleFeign.saveArticle(articleDto);
  }
}
